--[[ GEO SDK CORE :: RENDER MODULE ]]--
--[[
	author:		genesis.erode / The Anger
	version:	5
	
	
---	EXPORTS ---
	
	
	
---	CLASSES ---
	
===	render/render ===
		
		global render object G.render
		
		
	+++	property bool	<render>.enabled = true
		
		if not true, does not process any passes when <render>:all() is called.
		
		
	***	int		<render>:all()
		
		renders all child objects (passes) in sequence.
		returns number of passes successfully rendered.
		
		
	***	<pass>	<render>:newPass()
		
		creates a new render pass and returns a reference to it.
		
		
		
===	render/Ent ===
		
		Entity render properties <Ent>.render
		
		
	+++	property <Ent>	<render/Ent>.ent
		
		returns a reference to the parent entity, if one exists.
		
		
	+++	property bool	visible = true
		
		entity will not be rendered (by default) if it is not visible.
		
		
	***	virtual void	<render/Ent>:pass( <pass> )
		
		the raw geometry display function for an entity - Ent.render:pass( <pass> )
		
		
	***	virtual bool	<render/Ent>:before( <pass> )
		
		if this returns false, the pass does not proceed with rendering of this entity or its descendants in turn.
		
		by default it returns true if <Ent>.render.visible is true
		
		
	***	virtual void	<render/Ent>:after( <pass> )
		
		called after the pass tries to render a specific entity.
		if the associated 'before' call returned false, this function is not called.
		
		
		
===	render/pass ===
		
		render pass object.
		iterates over all entities in play and sends their geometry data to the engine to be drawn.
		
		
	+++	bool	<pass>.active = true
		
		pass will be skipped if not active
		
		
	***	bool	<pass>( void )
		
		triggers the render pass.
		
		if beforeRender does not return false, entities that are in play are rendered in-order.
		after the pass finishes it calls afterRender, and passes a bool indicating if any rendering was needed.
		
		for every entity rendered, a similar check is performed using entityBeforeRender and entityAfterRender calls.
		if an entity is not rendered, neither are its children.
		
		an entity is actually rendered with <pass>:renderEntity( <Ent> ).
		
		
	***	seq		<pass>:order( void )
		
		= sibling position within its parent, if parented (which it should be usually).
		
		by definition:
		<pass> = <pass>._parent[ <pass>:order() ]
		
		
	***	virtual void	<pass>:renderEntity( <Ent> )
		
		the internal call to render an entity.
		can be overridden.
		
		by default it calls: <Ent>.render:pass( <pass> )
		
		
	***	virtual bool	<pass>:beforeRender( void )
		
		before the pass begins rendering, it calls self:beforeRender().
		if this returns false, the pass does not proceed with rendering.
		
		by default it always returns true.
		
		
	***	virtual void	<pass>:afterRender( void )
		
		after the pass has finished rendering entities, it calls self:afterRender( was_somthing_rendered() ).
		if the associated 'beforeRender' call returned false, this function is not called.
		
		
	***	virtual bool	<pass>:entBeforeRender( <Ent> )
		
		before the pass renders a specific entity, it calls self:entBeforeRender( <Ent> ).
		if this returns false, the pass does not proceed with rendering of this entity or its descendants in turn.
		
		by default it calls <Ent>.render:before( <pass> ).
		
		
	***	virtual void	<pass>:entAfterRender( <Ent> )
		
		after the pass tries to render a specific entity, it calls self:entAfterRender( <Ent> ).
		if the associated 'entBeforeRender' call returned false, this function is not called.
		
		by default it calls <Ent>.render:after( <pass> ).
		
		
]]--
do
	--[[ GLOBALS ]]--
	
	local EntRenderEvents = {
		'beforePass',
		'onPass',
		'afterPass'
	}
	
	
	--[[ CLASSES ]]--
	
	local _mod_name = [=========================[render]=========================]
	
	G.core.mods[ _mod_name ] = {
		[ _mod_name ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			{
			},{
				[ [====[newPass]====] ] =
				function( this )
					if not G.core.is_gstype(this, 'render/render') then
						print( 'idiot alarm - attempted to create render/pass as a child object of a disallowed parent type' )
						return
					end
					return G.core.moveto( G.core.newObj('render/pass'), this )
				end;
				
				[ [====[all]====] ] =
				function( this )
					if not this.enabled then return 0 end
					
					local count = 0
					
					G.core.forSubObjects( this, function(pass)
						if not (
							G.core.is_gstype( pass, 'render/pass' ) and pass()
						) then
							return false
						end
						count = count + 1
						return true
					end)
					
					return count
				end
			},{
				[ [====[enabled]====] ] = true
			}
		),
		
		[ [================[pass]================] ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			{
				__call = function( this )
					if not this:beforeRender() then return false end
					
					G:forSubObjects(function(ent) return ent.render:execPipeline( this ) end)
					
					this:afterRender( true )
					return true
				end
			},{
				[ [====[order]====] ] = G.core.get_seq
			},{
				[ [====[beforeRender]====] ] =
				function()
					return true
				end;
				
				[ [====[afterRender]====] ] =
				function() end;
				
				
				-- [ [====[renderEntity]====] ] =
				-- function(this, ent)
					-- ent.render:pass( this )
				-- end;
				
				-- [ [====[entBeforeRender]====] ] =
				-- function(this, ent)
					-- return ent.render:before( this )
				-- end;
				
				-- [ [====[entAfterRender]====] ] =
				-- function(this, ent)
					-- ent.render:after( this )
				-- end;
				
				
				[ [====[active]====] ] = true
			}
		),
		
		[ [================[Ent]================] ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			nil,{
				[ [====[execPipeline]====] ] =
				function(this, pass)
					local pipe = this.pipeline
					local i, n, go = 1, #pipe, true
					while go and i <= n do i, go = i + 1, pipe[i](this, pass) end
					--print('execPipeline()')
					return go
				end;
				
				[ [====[buildPipeline]====] ] =
				function(this)
					-- reset render sequence
					local action, pipe = false, {}
					
					if this.visible then
						--print('buildPipeline()')
						for i = 1, #EntRenderEvents do
							action = this[EntRenderEvents[i]]
							if action then
								table.insert( pipe, action )
							end
						end
					end
					
					this.pipeline = pipe
					
					
					return true
				end
			},{
				[ [====[pipeline]====] ] = {};
				
				--[[
				[ [====[pass]====] ] =
				function(this, pass)
					-- todo: default entity render code
				end;
				]]--
				
				[ [====[visible]====] ] = true
			}
		)
	}
	
end
